﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;

namespace InfiniteGeek.Util
{
	internal static class ExtensionMethods
	{
		/// <summary>Removes a target string from the start of a source string.</summary>
		/// <param name="s">The source string.</param>
		/// <param name="target">The target string to remove.</param>
		/// <returns>A new string that doesn't start with the target string.</returns>
		[DebuggerStepThrough]
		public static String Clip(this String s, String target)
		{
			return s.StartsWith(target) ? s.Substring(target.Length) : s;
		}

		/// <summary>Shortcut syntax for using String.Format.</summary>
		/// <param name="format">A composite format string.</param>
		/// <param name="args">An object array that contains zero or more objects to format.</param>
		/// <returns>A copy of <paramref name="format"/> in which the format items have been replaced by the string representation of the corresponding objects in <paramref name="args"/>.</returns>
		/// <exception cref="ArgumentNullException"></exception>
		/// <exception cref="FormatException"></exception>
		[DebuggerStepThrough]
		public static String FormatWith(this String format, params Object[] args)
		{
			return String.Format(format, args);
		}

		/// <summary>
		/// Shortcut syntax for using string.Format.
		/// </summary>
		/// <remarks>Throwing an <see cref="ArgumentNullException"/> <see langword="null"/>format == null is redundant.</remarks>
		/// <param name="format">A composite format string.</param>
		/// <param name="provider">An object that supplies culture-specific formatting information.</param>
		/// <param name="args">An object array that contains zero or more objects to format.</param>
		/// <returns>A copy of <paramref name="format"/> in which the format items have been replaced by the string representation of the corresponding objects in <paramref name="args"/>.</returns>
		/// <exception cref="ArgumentNullException"></exception>
		/// <exception cref="FormatException"></exception>
		[DebuggerStepThrough]
		public static string FormatWith(this String format, IFormatProvider provider, params Object[] args)
		{
			return String.Format(provider, format, args);
		}
		
		/// <summary>Gets attributes of <typeparamref name="TAttribute"/> from a FieldInfo, PropertyInfo, or MemberInfo, <paramref name="item"/>.</summary>
		/// <typeparam name="TAttribute">The type of the custom attribute to retrieve.</typeparam>
		/// <param name="item">ICustomAttributeProvider instance to search for attribute of type <typeparamref name="TAttribute"/>.</param>
		/// <returns><typeparamref name="TAttribute"/>Attribute, <see langword="null"/> if not found.</returns>
		[DebuggerStepThrough]
		public static TAttribute GetAttribute<TAttribute>(this ICustomAttributeProvider item) where TAttribute : Attribute
		{
			return item.GetCustomAttributes(typeof(TAttribute), false).FirstOrDefault() as TAttribute;
		}

		/// <summary>Gets attributes of <typeparamref name="TAttribute"/> from an object instance, <paramref name="item"/>.</summary>
		/// <typeparam name="TAttribute">The type of the custom attribute to retrieve.</typeparam>
		/// <param name="item">Object instance to get attribute of type <typeparamref name="TAttribute"/> from.</param>
		/// <returns><typeparamref name="TAttribute"/>Attribute, <see langword="null"/> if not found.</returns>
		[DebuggerStepThrough]
		public static TAttribute GetAttribute<TAttribute>(this Object item) where TAttribute : Attribute
		{
			return GetAttribute<TAttribute>(item, false);
		}

		/// <summary>Gets first attribute of <typeparamref name="TAttribute"/> from an object instance, <paramref name="item"/>.</summary>
		/// <typeparam name="TAttribute">The type of the custom attribute to retrieve.</typeparam>
		/// <param name="item">Object instance to search for attribute of type <typeparamref name="TAttribute"/>.</param>
		/// <param name="inherit">When true, the underlying GetCustomAttributes call searches the inheritance hierarchy for the <see cref="Attribute"/>.</param>
		/// <returns>Returns the first <see cref="Attribute"/> of type <typeparamref name="TAttribute"/>, <see langword="null"/> if not found.</returns>
		public static TAttribute GetAttribute<TAttribute>(this Object item, Boolean inherit) where TAttribute : Attribute
		{
			Type itemtype = item.GetType();
			TAttribute attrib = itemtype.GetCustomAttributes(typeof(TAttribute), inherit).FirstOrDefault() as TAttribute;

			if (item is ICustomAttributeProvider) // PropertyInfo, MethodInfo, FieldInfo
			{
				attrib = ((ICustomAttributeProvider)item).GetCustomAttributes(typeof(TAttribute), inherit) as TAttribute;
			}

			if (attrib == null)
			{
				FieldInfo itemfield = itemtype.GetField(item.ToString());
				if (itemfield != null)
				{
					attrib = itemfield.GetCustomAttributes(typeof(TAttribute), inherit).FirstOrDefault() as TAttribute;
				}

				if (attrib == null)
				{
					PropertyInfo itemprop = itemtype.GetProperty(item.ToString());
					if (itemprop != null)
					{
						attrib = itemprop.GetCustomAttributes(typeof(TAttribute), inherit).FirstOrDefault() as TAttribute;
					}
				}
			}

			return attrib;
		}
		
		/// <summary>Determines whether the specified item has attribute. Search the inheritance tree by default.</summary>
		/// <typeparam name="TAttribute">The type of the custom attribute to retrieve.</typeparam>
		/// <param name="item">Object instance to get attribute of type <typeparamref name="TAttribute"/> from.</param>
		/// <returns>
		/// 	<c>true</c> if the specified item has the attribute; otherwise, <c>false</c>.
		/// </returns>
		[DebuggerStepThrough]
		public static Boolean HasAttribute<TAttribute>(this Object item) where TAttribute : Attribute
		{
			return HasAttribute<TAttribute>(item, true);
		}

		/// <summary>Determines whether the specified item has attribute, optionally searches the inheritance tree.</summary>
		/// <typeparam name="TAttribute">The type of the custom attribute to retrieve.</typeparam>
		/// <param name="item">Object instance to get attribute of type <typeparamref name="TAttribute"/> from.</param>
		/// <param name="inherit">When true, the underlying GetCustomAttributes call searches the inheritance hierarchy for the <see cref="Attribute"/>.</param>
		/// <returns>
		/// 	<c>true</c> if the specified item has the attribute; otherwise, <c>false</c>.
		/// </returns>
		public static Boolean HasAttribute<TAttribute>(this Object item, Boolean inherit) where TAttribute : Attribute
		{
			if (item is ICustomAttributeProvider)
			{
				return (item as ICustomAttributeProvider).GetAttribute<TAttribute>() != null;
			}

			Type itemtype = item.GetType();
			TAttribute attrib = itemtype.GetCustomAttributes(typeof(TAttribute), inherit).FirstOrDefault() as TAttribute;
			return attrib != null;
		}

		/// <summary>Returns an array of custom attributes applied to this member and identified by <typeparamref name="TAttribute"/> </summary>
		/// <typeparam name="TAttribute">The type of the custom attributes to retrieve.</typeparam>
		/// <param name="item">Object instance to get attribute of type <typeparamref name="TAttribute"/> from.</param>
		/// <returns>An array of custom attributes applied to this member, or an array with zero elements if no attributes assignable to attributeType have been applied.</returns>
		public static Attribute[] GetAttributes<TAttribute>(this Object item) where TAttribute : Attribute
		{
			return item.GetAttributes<TAttribute>(true);
		}

		/// <summary>Returns an array of custom attributes applied to this member and identified by <typeparamref name="TAttribute"/> </summary>
		/// <remarks>This method ignores the inherit parameter for properties and events. To search the inheritance chain for attributes on properties and events, use the appropriate overloads of the Attribute.GetCustomAttributes method.</remarks>
		/// <typeparam name="TAttribute">The type of the custom attributes to retrieve.</typeparam>
		/// <param name="item">Object instance to get attribute of type <typeparamref name="TAttribute"/> from.</param>
		/// <param name="inherit">True to search this member's inheritance chain to find the attributes; otherwise, false. This parameter is ignored for properties and events; see Remarks.</param>
		/// <returns>An array of custom attributes applied to this member, or an array with zero elements if no attributes assignable to attributeType have been applied.</returns>
		public static Attribute[] GetAttributes<TAttribute>(this Object item, Boolean inherit) where TAttribute : Attribute
		{
			Type itemtype = item.GetType();
			return itemtype.GetCustomAttributes(typeof(TAttribute), inherit) as TAttribute[];
		}

		/// <summary>Emulates <see cref="Nullable"/>&lt;T&gt;.HasValue for String, since String is already nullable. Equivalent to !String.IsNullOrEmpty().</summary>
		/// <param name="s">The string to test.</param>
		/// <returns><c>true</c> if the value parameter is <see langword="null" /> or an empty string (""); otherwise, <c>false</c>.</returns>
		[DebuggerStepThrough]
		public static Boolean HasValue(this String s)
		{
			return !String.IsNullOrEmpty(s);
		}

		[DebuggerStepThrough]
		public static IEnumerable<String> TrimAll(this IEnumerable<String> strings) {
			return strings.Select(s => s.Trim());
		}

	}
}